package org.jugile.util;

import java.lang.reflect.Method;
import java.util.Comparator;
import java.util.List;


public class IDOComparator extends Jugile implements Comparator<Object> {
	
	private int factors[];
	private GetMethod methods[];
	private String flds[];
	private String params[];

	public IDOComparator(String crit) {
		List<String> tmp = split(crit,',');
		factors = new int[tmp.size()];
		methods = new GetMethod[tmp.size()];
		flds = new String[tmp.size()];
		params = new String[tmp.size()];

		for (int i = 0; i < tmp.size(); i++) {
			String fld = tmp.get(i);
			if (fld.indexOf(':') > 0) {
				String p[] = fld.split(":");
				if (p.length != 2) fail("syntax error in sort fld: " + fld);
				fld = p[0];
				params[i] = p[1];
			}
			if (fld.startsWith("-")) {
				flds[i] = fld.substring(1);
				factors[i] = -1;
			} else {
				flds[i] = fld;
				factors[i] = 1;
			}				
		}
	}

	private GetMethod getMethod(int i, Object o) {
		if (methods[i] == null) methods[i] = new GetMethod(o.getClass(), flds[i], params[i]);
		return methods[i];
	}

	public int compare(Object o1, Object o2) {
		if (o1 == null && o2 == null) return 0;
		if (o1 == null) return -factors[0];
		if (o2 == null) return factors[0];
		int res = 0;

		for (int i = 0; i < factors.length; i++) {
			GetMethod m = getMethod(i, o1);
			Comparable v1 = m.invoke(o1);
			Comparable v2 = m.invoke(o2);
			if (v1 == null) return -factors[i];
			if (v2 == null) return factors[i];
			if (v1 instanceof String) {
				res = ((String)v1).compareToIgnoreCase((String)v2);
			} else {
				res = v1.compareTo(v2);
			}
			if (res != 0) return res*factors[i];
		}

		return res;
	}

	public static class GetMethod {
		private Method m = null;
		private String fld = null;
		private String param = null;
		
		public GetMethod(Class cl, String fld, String p) {
			try {
				String name = "get"+up(fld);
				if ("id".equals(fld)) name = "id";
				Class[] attrs = null;
				if (p != null) {
					param = p;
					attrs = new Class[1];
					attrs[0] = String.class;
				}
				m = cl.getMethod(name, attrs);
			} catch (NoSuchMethodException nsme){ }
			if (m == null) {
				try { 
					Class[] params = new Class[] { String.class }; 
					m = cl.getMethod("get", params);
				} catch (Exception e) { fail(e);}
				this.fld = fld;
			}
		}
		
		public Comparable invoke(Object o) {
			try {
				if (fld == null && param == null) return (Comparable)m.invoke(o,(Object[])null);
				if (param != null) {
					Object[] params = new Object[]{param};
					return (Comparable)m.invoke(o,params);
				}
				Object[] params = new Object[]{fld};
				return (Comparable)m.invoke(o,params);
			} catch(Exception e) {fail(e);} 
			return null;
		}		
	}

}

